package io.nutz.ipd;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.nutz.boot.NbApp;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Encoding;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.lang.tmpl.Tmpl;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Fail;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.plugins.ip2region.DataBlock;
import org.nutz.plugins.ip2region.DbSearcher;

@IocBean(create = "init")
public class MainLauncher {

    @Inject
    protected PropertiesProxy conf;

    protected DbSearcher searcher;

    @At("/")
    @Ok("void")
    public void index(HttpServletRequest req, HttpServletResponse resp, @Param("ip") String ip) throws IOException {
        if (isCurlLike(req.getHeader("User-Agent"))) {
            resp.getWriter().write(ip(null));
        } else {
            String str = Streams.readAndClose(Streams.utf8r(getClass().getClassLoader().getResourceAsStream("template/index.html")));
            NutMap map = result(ip);
            map.put("host", req.getHeader("Host"));
            String re = Tmpl.exec(str, map);
            resp.setContentType("text/html");
            byte[] buf = re.getBytes(Encoding.CHARSET_UTF8);
            resp.setContentLength(buf.length);
            resp.getOutputStream().write(buf);
        }
    }

    @At("/json")
    @Ok("json")
    public NutMap json(@Param("ip") String ip) {
        return result(ip);
    }

    @At("/ip")
    @Ok("raw")
    public String ip(@Param("ip") String ip) {
        return result(ip).getString("ip") + "\r\n";
    }

    @At("/city")
    @Ok("raw")
    public String city(@Param("ip") String ip) {
        return result(ip).getString("city") + "\r\n";
    }

    @At("/country")
    @Ok("raw")
    public String country(@Param("ip") String ip) {
        return result(ip).getString("country") + "\r\n";
    }

    @Ok("json")
    @Fail("http:404")
    @At("/port/?")
    public NutMap port(int port) {
        NutMap map = result(null);
        String ip = map.getString("ip");
        Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress(ip, port), 1000);
            map.put("reachable", true);
        }
        catch (Throwable e) {
            map.put("reachable", false);
        }
        finally {
            Streams.safeClose(socket);
        }
        map.put("port", port);
        return map;
    }

    protected NutMap result(String ip) {
        if (Strings.isBlank(ip)) {
            ip = Lang.getIP(Mvcs.getReq());
        }
        String[] addrArray = ip.split("\\.");
        long ipDecimal = 0;
        for (int i = 0; i < addrArray.length; i++) {
            int power = 3 - i;
            ipDecimal += ((Integer.parseInt(addrArray[i]) % 256 * Math.pow(256, power)));
        }
        NutMap map = new NutMap();
        map.put("ip", ip);
        map.put("ip_decimal", ipDecimal);

        try {
            DataBlock block = searcher.memorySearch(ip);
            String region = block.getRegion();
            String[] tmp = region.split("\\|");
            map.put("region", region);
            // 国家
            map.put("country", tmp[0]);
            // 地区
            map.put("zone", "0".equals(tmp[1]) ? "未知" : tmp[1]);
            // 省份
            map.put("province", "0".equals(tmp[2]) ? "未知" : tmp[2]);
            // 城市
            map.put("city", "0".equals(tmp[3]) ? "未知" : tmp[3]);
            // 运营商
            map.put("isp", "0".equals(tmp[4]) ? "未知" : tmp[4]);
        }
        catch (IOException e) {}
        return map;
    }

    protected boolean isCurlLike(String userAgent) {
        if (Strings.isBlank(userAgent))
            return false;
        String[] tmp = userAgent.split("/");
        if (tmp.length > 1) {
            // nop
        } else {
            tmp = userAgent.split(" ", 2);
        }
        String ua = tmp[0].toLowerCase();
        switch (ua) {
        case "curl":
        case "httpie":
        case "wget":
        case "fetch libfetch":
        case "go":
        case "go-http-client":
        case "ddclient":
            return true;
        default:
            return false;
        }
    }

    public void init() {
        searcher = new DbSearcher();
    }

    public static void main(String[] args) throws Exception {
        new NbApp().setArgs(args).setPrintProcDoc(true).run();
    }

}
